package edu.pku.sei.autounit.reflect.java;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.TypeVariable;
import java.util.Iterator;
import java.util.Map;

import edu.pku.sei.autounit.reflect.AbstractContextType;
import edu.pku.sei.autounit.reflect.Class;
import edu.pku.sei.autounit.reflect.Context;
import edu.pku.sei.autounit.reflect.Interface;
import edu.pku.sei.autounit.reflect.Type;
import edu.pku.sei.autounit.reflect.TypeParameter;

public class JavaInterface extends AbstractContextType implements Interface{
	
	private String simpleName;
	private String fullName;
	private String packageName;
	private Interface [] interfaces;
	private java.lang.Class<?> javaClass;
	private TypeFactory factory;
	
	public JavaInterface(java.lang.Class<?> javaClass,TypeFactory factory){
		simpleName = javaClass.getSimpleName();
		fullName = javaClass.getName();
		packageName = javaClass.getPackage().getName();
		this.javaClass = javaClass;
		this.factory = factory;
		
		TypeVariable<?> [] tps = javaClass.getTypeParameters();
		typeParameters = new TypeParameter[tps.length];
		for(int i =0 ;i<tps.length;i++){
			typeParameters[i] = new JavaTypeParameter(tps[i].getName());
			
			java.lang.reflect.Type []tt = tps[i].getBounds();
			Type [] bs = new Type[tt.length];
			for(int j = 0; j<tt.length; j++)
				bs[j] = factory.getType(tt[j],this);
			typeParameters[i].setBounds(bs);
		}
	}
	
	public JavaInterface(java.lang.Class<?> javaClass,Type[] contexts,TypeFactory factory){

		this.simpleName = javaClass.getSimpleName();
		this.fullName = javaClass.getName();
		this.packageName = javaClass.getPackage().getName();
		this.factory = factory;
		
		TypeVariable<?> [] tps = javaClass.getTypeParameters();
		typeParameters = new TypeParameter[tps.length];
		
		for(int i =0 ;i<tps.length;i++){
			typeParameters[i] = new JavaTypeParameter(tps[i].getName());
			if(i<contexts.length)
				typeParameters[i].setContext(contexts[i]);
			
			java.lang.reflect.Type []tt = tps[i].getBounds();
			Type [] bs = new Type[tt.length];
			for(int j = 0; j<tt.length; j++)
				bs[j] = factory.getType(tt[j],this);
			typeParameters[i].setBounds(bs);
		}
	}
	
	public JavaInterface(ParameterizedType p,Context con,TypeFactory factory){
		this.javaClass = (java.lang.Class<?>)p.getRawType();
		this.simpleName = javaClass.getSimpleName();
		this.fullName = javaClass.getName();
		this.packageName = javaClass.getPackage().getName();
		this.factory = factory;
		
		TypeVariable<?> [] tps = javaClass.getTypeParameters();
		typeParameters = new TypeParameter[tps.length];
		java.lang.reflect.Type [] contexts = p.getActualTypeArguments();
		for(int i =0 ;i<tps.length;i++){
			typeParameters[i] = new JavaTypeParameter(tps[i].getName());
			if(i<contexts.length)
				typeParameters[i].setContext(factory.getType(contexts[i],con));
			
			java.lang.reflect.Type []tt = tps[i].getBounds();
			Type [] bs = new Type[tt.length];
			for(int j = 0; j<tt.length; j++)
				bs[j] = factory.getType(tt[j],this);
			typeParameters[i].setBounds(bs);
		}
	}
	
	
	public String getFullName() {
		return fullName;
	}

	
	public String getName(Map<String, String> map) {
		StringBuffer sb = new StringBuffer();
		if(fullName.startsWith("java.lang"))
			sb.append(simpleName);
		else{
			String f = map.get(simpleName);
			if(f == null){
				map.put(simpleName, fullName);
				sb.append(simpleName);
			}
			else if(f.equals(fullName))
				sb.append(simpleName);
			else
				sb.append(fullName);
		}

		Type [] contexts = getContexts();
		if(contexts.length == 0)
			return sb.toString();
		
		sb.append("<");
		for(int i=0;i<contexts.length-1;i++)
			sb.append(contexts[i].getName(map)+",");
		sb.append(contexts[contexts.length-1].getName(map)+">");
		return sb.toString();
	}
	

	
	public Interface[] getInterfaces() {
		if(interfaces == null){
			java.lang.reflect.Type []is = javaClass.getGenericInterfaces();
			interfaces = new Interface[is.length];
			for(int i=0;i<is.length;i++){
				interfaces[i] = (Interface) factory.getType(is[i],this);
			}
		}
		return interfaces;
	}

	
	public String getPackageName() {
		return packageName;
	}

	
	public Iterator<Class> getSubClasses() {
		return new Iterator<Class>(){

			
			public boolean hasNext() {
				return false;
			}

			
			public Class next() {
				return null;
			}

			
			public void remove() {
			}
			
		};
	}
}
